<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  
  <title>张量操作 | Hexo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="前言在使用 pytorch 的过程中，遇到了较多张量操作的相关问题，这篇 blog 主要是为了对相关的操作进行整合、梳理，以求在写代码时少遇到几个 bug pytorch 张量广播机制PyTorch 张量的广播（Broadcasting）是一种机制，它允许在不进行显示复制数据的情况下，对形状不同但满足一定条件的张量进行元素级的运算。这使得你可以在不显式地扩展张量的维度的情况下，执行一些操作，从而简">
<meta property="og:type" content="article">
<meta property="og:title" content="张量操作">
<meta property="og:url" content="http://example.com/2023/09/05/%E5%BC%A0%E9%87%8F%E6%93%8D%E4%BD%9C/index.html">
<meta property="og:site_name" content="Hexo">
<meta property="og:description" content="前言在使用 pytorch 的过程中，遇到了较多张量操作的相关问题，这篇 blog 主要是为了对相关的操作进行整合、梳理，以求在写代码时少遇到几个 bug pytorch 张量广播机制PyTorch 张量的广播（Broadcasting）是一种机制，它允许在不进行显示复制数据的情况下，对形状不同但满足一定条件的张量进行元素级的运算。这使得你可以在不显式地扩展张量的维度的情况下，执行一些操作，从而简">
<meta property="og:locale" content="en_US">
<meta property="article:published_time" content="2023-09-05T05:18:00.000Z">
<meta property="article:modified_time" content="2023-09-05T06:12:26.415Z">
<meta property="article:author" content="John Doe">
<meta name="twitter:card" content="summary">
  
    <link rel="alternate" href="/atom.xml" title="Hexo" type="application/atom+xml">
  
  
    <link rel="shortcut icon" href="/favicon.png">
  
  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/typeface-source-code-pro@0.0.71/index.min.css">

  
  
<link rel="stylesheet" href="/css/style.css">

  
    
<link rel="stylesheet" href="/fancybox/jquery.fancybox.min.css">

  
  
<meta name="generator" content="Hexo 6.3.0"></head>

<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/" id="logo">Hexo</a>
      </h1>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"><span class="fa fa-bars"></span></a>
        
          <a class="main-nav-link" href="/">Home</a>
        
          <a class="main-nav-link" href="/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
        
          <a class="nav-icon" href="/atom.xml" title="RSS Feed"><span class="fa fa-rss"></span></a>
        
        <a class="nav-icon nav-search-btn" title="Search"><span class="fa fa-search"></span></a>
      </nav>
      <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="http://example.com"></form>
      </div>
    </div>
  </div>
</header>

      <div class="outer">
        <section id="main"><article id="post-张量操作" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/09/05/%E5%BC%A0%E9%87%8F%E6%93%8D%E4%BD%9C/" class="article-date">
  <time class="dt-published" datetime="2023-09-05T05:18:00.000Z" itemprop="datePublished">2023-09-05</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 class="p-name article-title" itemprop="headline name">
      张量操作
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>在使用 pytorch 的过程中，遇到了较多张量操作的相关问题，这篇 blog 主要是为了对相关的操作进行整合、梳理，以求在写代码时少遇到几个 bug</p>
<h2 id="pytorch-张量广播机制"><a href="#pytorch-张量广播机制" class="headerlink" title="pytorch 张量广播机制"></a>pytorch 张量广播机制</h2><p>PyTorch 张量的广播（Broadcasting）是一种机制，它允许在不进行显示复制数据的情况下，对形状不同但满足一定条件的张量进行元素级的运算。这使得你可以在不显式地扩展张量的维度的情况下，执行一些操作，从而简化代码并提高效率。</p>
<p>广播的原则是：如果两个张量在某一维度上的形状大小相同，或其中一个张量的大小为 1，那么它们可以在该维度上进行广播。广播会自动扩展大小为 1 的维度，使得两个张量的维度相同，然后执行元素级操作。</p>
<p>注意到，广播一般是某一维为 1，或者某一个量是标量时才可以进行。</p>
<p>从定义而言不难理解，但实际应用中需要注意的地方挺多：<br>基础用法： 假设 pts:[bs, N, 3], offset[3]，即我们需要对所有的点进行 x, y, z 统一的一个平移，那么这段代码最不容易被误解的形式可以写成下面这样：<br><code>pts = pts + offset[None, None]</code><br>在这个过程中，offset 的 shaep 由[3]首先被扩展为[1, 1, 3]， 接着就能够直接由广播机制与 pts 相加，达到偏差的效果</p>
<h2 id="pytorch-张量乘法"><a href="#pytorch-张量乘法" class="headerlink" title="pytorch 张量乘法"></a>pytorch 张量乘法</h2><p>pytorch 官方定义的乘法具体可以写为以下几类：</p>
<ol>
<li>*的乘法</li>
<li>矩阵 or 张量乘法</li>
</ol>
<h3 id="逐个元素相乘"><a href="#逐个元素相乘" class="headerlink" title="逐个元素相乘 *"></a>逐个元素相乘 *</h3><p>这种乘法是最直观的一种乘法类型，它能够将两个形状相同（或能够通过广播机制扩展到相同形状）的张量，按照位置逐个元素相乘，得到的结果依然是同样的形状。<br>由于这种方法与 pytorch 经常进行的矩阵运算形式不太相同，所以在实际应用中，除基础的标量放缩外，我们很少使用这种乘法方式。<br>这里举一个向量点乘的例子：<br>假设 A:[3] &#x3D; (0.1, 0.2, 0.3), B:[3] &#x3D; (0.4, 0.5, 0.6)<br>则 A 与 B 的点积 dot(A, B) &#x3D; A * B &#x3D; (0.04, 0.1, 0.18)</p>
<h3 id="矩阵乘法"><a href="#矩阵乘法" class="headerlink" title="矩阵乘法 @"></a>矩阵乘法 @</h3><p>torch.mul 用的比较少，用于两个同维度矩阵逐像素点乘（等价于 *）<br>torch.mm 表示二维矩阵乘法， 只支持 (l, m) 与 (m, n) 相乘，得到维数为 (l, n)的这种类似的二维矩阵运算。<br>torch.bmm 则是在 torch.mm 的基础上对 batch 做了拓展，支持了 (b, l, m) 与 (b, m, n)相乘，得到维数为(b, l, n)的矩阵运算。<br>torch.mv 则用于矩阵乘向量的形式 (l, m) 与 (m) 得到 (l) ，实际上类似于将后面的向量直接扩展为(m, 1)然后进行矩阵运算的结果，因此这个函数也比较少用。</p>
<p>比较重要的是 torch.matmul 函数，这个函数囊括了上面的除 torch.mul 之外的所有函数。因此这个函数值得着重介绍。<br>torch.matmul 中内含了广播机制，因此对于各种张量的运算都能够类似的解决<br>如类似于 torch.mm 的二维矩阵乘法： (l, m) (m, n) -&gt; (l, n)<br>类似于 torch.bmm 的扩展 batch 的矩阵乘法： (b, l, m) (b, m, n) -&gt; (b, l, n)<br>以及加上广播机制的乘法： (l, m) (b, m, n) -&gt; (b, l, n) 或 (l, m) (m) -&gt; (l) 或 (b, c, l, m) (b, c, m, n) -&gt; (b, c, l, n)等形式</p>
<p>@ 表示常规的数学上定义的矩阵相乘， 其作用与 torch.matmal 类似。</p>
<h3 id="einsum-记法"><a href="#einsum-记法" class="headerlink" title="einsum 记法"></a>einsum 记法</h3><p>torch.matmal 的功能虽然强大，但有时也会容易引起广播上的歧义或由于使用失误导致运算上的问题出现，因此，本文作者认为 einsum 函数是解决这些复杂张量运算的有效工具。<br>API: <code>torch.einsum(equation, *operands)</code><br>求和：result &#x3D; torch.einsum(“ij-&gt;”, a)<br>矩阵乘法： result &#x3D; torch.einsum(“ij,jk-&gt;ik”, a, b)<br>批量矩阵乘法： result &#x3D; torch.einsum(“bij,bjk-&gt;bik”, a, b)<br>梯度计算： result &#x3D; torch.einsum(“i,i-&gt;”, a, b)</p>
<p>值得注意的是， einsum 在角标的选择上十分灵活，支持如：<br>torch.einsum(“bij,jk-&gt;bik”, a, b)<br>torch.einsum(“bjk, ij-&gt; bik”, a, b)<br>等等形式。</p>
<h2 id="pytorch-视图机制"><a href="#pytorch-视图机制" class="headerlink" title="pytorch 视图机制"></a>pytorch 视图机制</h2><p>PyTorch 的视图机制是一种在不复制底层数据的情况下创建张量的方式，允许您以不同的方式查看相同的底层数据。这对于节省内存并提高计算效率非常有用。PyTorch 的视图机制包括以下几个函数和属性：</p>
<ul>
<li>view() 方法用于创建一个具有相同数据但形状不同的张量。它适用于原始张量的连续子序列，并且不能改变张量的总元素数。view() 方法仅用于连续内存块。</li>
<li>reshape() 方法类似于 view()，但它可以处理非连续内存块。它试图返回一个新的张量，该张量与原始张量共享数据，但形状可能不同。如果原始张量的内存布局不允许重新形状，reshape() 将返回一个副本。</li>
<li>squeeze() 用于删除张量中大小为 1 的维度。unsqueeze() 用于在指定位置插入大小为 1 的维度。</li>
<li>expand() 方法用于在指定维度上扩展张量的形状，但不会复制数据。它使用广播机制来扩展形状。</li>
</ul>
<p>视图机制允许您通过创建不同的张量视图，以不同的方式查看底层数据，而无需复制数据。这对于在不同形状之间共享数据和减少内存消耗非常有用。但请注意，在某些情况下，例如非连续内存块或无法重塑的情况下，这些操作可能会生成副本而不是视图。</p>
<h2 id="torch-expand-torch-repeat"><a href="#torch-expand-torch-repeat" class="headerlink" title="torch.expand &amp; torch.repeat"></a>torch.expand &amp; torch.repeat</h2><p><code>torch.expand()</code> 用于在现有张量的指定维度上扩展张量的形状，使其匹配目标形状。它通过在指定维度上重复元素来实现形状的扩展，但并不复制数据，只是改变了张量的视图。因此，它不会增加内存消耗。</p>
<p><code>torch.repeat()</code> 用于在指定维度上重复复制张量的内容，从而实现形状的扩展。它会复制原始数据，因此可能会导致内存消耗增加。</p>
<p>总结区别：</p>
<ul>
<li><code>torch.expand()</code> 主要用于在指定维度上扩展张量形状，不复制数据，不增加内存消耗。</li>
<li><code>torch.repeat()</code> 主要用于在指定维度上重复复制张量内容，会复制数据，可能增加内存消耗。</li>
</ul>
<h2 id="torch-gather-函数的使用"><a href="#torch-gather-函数的使用" class="headerlink" title="torch.gather 函数的使用"></a>torch.gather 函数的使用</h2><p>torch.gather 函数是对取索引非常方便的一种使用方法，它代表了从 A 中按照 B 相对应的索引来取出对应位置的元素，但由于其对函数的张量维数有较高的要求，因此在使用过程中需要额外注意。</p>
<h2 id="torch-nn-functional-grid-sample-函数的使用"><a href="#torch-nn-functional-grid-sample-函数的使用" class="headerlink" title="torch.nn.functional.grid_sample 函数的使用"></a>torch.nn.functional.grid_sample 函数的使用</h2>
      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/09/05/%E5%BC%A0%E9%87%8F%E6%93%8D%E4%BD%9C/" data-id="clm5v0ig40000z4w74x0sh9zo" data-title="张量操作" class="article-share-link"><span class="fa fa-share">Share</span></a>
      
      
      
    </footer>
  </div>
  
    
<nav id="article-nav">
  
  
    <a href="/2023/09/05/first-try/" id="article-nav-older" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Older</strong>
      <div class="article-nav-title">first try</div>
    </a>
  
</nav>

  
</article>


</section>
        
          <aside id="sidebar">
  
    

  
    

  
    
  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Archives</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2023/09/">September 2023</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Recent Posts</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/2023/09/05/%E5%BC%A0%E9%87%8F%E6%93%8D%E4%BD%9C/">张量操作</a>
          </li>
        
          <li>
            <a href="/2023/09/05/first-try/">first try</a>
          </li>
        
          <li>
            <a href="/2023/09/05/hello-world/">Hello World</a>
          </li>
        
      </ul>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      
      &copy; 2023 John Doe<br>
      Powered by <a href="https://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer>

    </div>
    <nav id="mobile-nav">
  
    <a href="/" class="mobile-nav-link">Home</a>
  
    <a href="/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    


<script src="/js/jquery-3.6.4.min.js"></script>



  
<script src="/fancybox/jquery.fancybox.min.js"></script>




<script src="/js/script.js"></script>





  </div>
</body>
</html>